#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_vfs_fat.h"
#include "esp_spiffs.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#if SOC_SDMMC_HOST_SUPPORTED
#include "driver/sdmmc_host.h"
#endif
#include "sdmmc_cmd.h"
#include "mount.h"

static const char *TAG = "example_mount";

#ifdef CONFIG_EXAMPLE_MOUNT_SD_CARD

esp_err_t example_mount_storage(const char *base_path)
{
    ESP_LOGI(TAG, "Init SD card!");
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        #ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_SDCARD_FAILED
        .format_if_mount_failed = true,
        #elif
        .format_if_mount_failed = false,
        #endif
        .max_files = 5,
        .allocation_unit_size = 16 * 1024,
    };

    esp_err_t ret;
    sdmmc_card_t *card;


    #ifdef CONFIG_EXAMPLE_USE_SDMMC_HOST

    ESP_LOGI(TAG, "Using SDMMC Peripheral");

    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    slot_config.width = 4;

    #ifdef SOC_SDMMC_USER_GPIO_MATRIX

    slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
    slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
    .slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
    .slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
    .slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
    .slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;

    #endif //SOC_SDMMC_USER_GPIO_MATRIX

    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
    ret = esp_vfs_fat_sdmmc_mount(base_path, &host, &slot_config, &mount_config, &card);

    #else  // CONFIG_EXAMPLE_USE_SDMMC_HOST
    ESP_LOGI(TAG, "Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = CONFIG_EXAMPLE_PIN_MOSI,
        .miso_io_num = CONFIG_EXAMPLE_PIN_MISO,
        .sclk_io_num = CONFIG_EXAMPLE_PIN_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };

    ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
    if(ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to init bus");
        return ret;
    }

    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = CONFIG_EXAMPLE_PIN_CS;
    slot_config.host_id = host.slot;

    ret = esp_vfs_fat_sdspi_mount(base_path, &host, &slot_config, &mount_config,&card);

    #endif //CONFIG_EXAMPLE_USE_SDMMC_HOST

    if(ret != ESP_OK)
    {
        if(ret == ESP_FAID)
        {
            ESP_LOGE(TAG,"Failed to mount filesystem. "
                "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        }
        else
        {
             ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return ret;
    }   
    sdmmc_card_print_info(stdout, card);

    return ESP_OK;
}

#else


esp_err_t example_mount_storage(const char *base_path)
{
    ESP_LOGI(TAG, "Init SPIFFS");

    esp_vfs_spiffs_conf_t conf = {
        .base_path = base_path,
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = true
    };

    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if(ret != ESP_OK)
    {
        if(ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        }
        else if(ret == ESP_ERR_NOT_FOUND)
        {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to init SPIFFS (%s)", esp_err_to_name(ret));
        }
        return ret;
    }

    size_t total = 0,used = 0;

    ret = esp_spiffs_info(NULL, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
        return ret;
    }

    ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    return ESP_OK;
}

#endif